﻿using System;

namespace SpatialHashLib.Topology
{
    public struct Coord : IEquatable<Coord>, ISpatialKey
    {
        static Coord empty = new Coord(double.NaN, double.NaN);

        public static Coord Empty { get { return empty; } }

        double x;
        double y;

        public Coord(double x, double y)
        {
            this.x = x;
            this.y = y;
        }

        public bool IsEmpty
        {
            get { return double.IsNaN(x) || double.IsNaN(y); }
        }

        public double X
        {
            get { return x; }
        }

        public double Y
        {
            get { return y; }
        }

        public Extent Extent
        {
            get { return new Extent(this, DoubleExtensions.GeneralEpsilon, DoubleExtensions.GeneralEpsilon); }
        }

        public bool Equals(Coord other)
        {
            return
                x.EpsilonEquals(other.x) &&
                y.EpsilonEquals(other.y)
                ;
        }

        public override bool Equals(object obj)
        {
            if (obj is Coord)
                return Equals((Coord)obj);

            return false;
        }

        public override int GetHashCode()
        {
            return new HashFunction().Hash(x).Hash(y).HashCode;
        }

        public double Distance
        {
            get { return Math.Sqrt(SquaredDistance); }
        }

        public double SquaredDistance
        {
            get { return x * x + y * y; }
        }

        public override string ToString()
        {
            return "(" + x.ToString() + ", " + y.ToString() + ")";
        }

        public double SquaredDistanceTo(Coord coord)
        {
            double dx = x - coord.x;
            double dy = y - coord.y;

            return dx * dx + dy * dy;
        }

        public double DistanceTo(Coord coord)
        {
            return Math.Sqrt(SquaredDistanceTo(coord));
        }

        public bool Intersects(Extent extent)
        {
            return
                x >= extent.Min.X && x <= extent.Max.X &&
                y >= extent.Min.Y && y <= extent.Max.Y;
        }

        #region Operator overloads

        public static Coord operator +(Coord a, Coord b)
        {
            return new Coord(a.x + b.x, a.y + b.y);
        }

        public static Coord operator -(Coord a, Coord b)
        {
            return new Coord(a.x - b.x, a.y - b.y);
        }

        public static Coord operator -(Coord a)
        {
            return new Coord(-a.x, -a.y);
        }

        public static Coord operator +(Coord a, double scalar)
        {
            return new Coord(a.x + scalar, a.y + scalar);
        }

        public static Coord operator -(Coord a, double scalar)
        {
            return new Coord(a.x - scalar, a.y - scalar);
        }

        public static Coord operator *(Coord a, double scalar)
        {
            return new Coord(a.x * scalar, a.y * scalar);
        }

        public static Coord operator *(double scalar, Coord a)
        {
            return a * scalar;
        }

        public static Coord operator /(Coord a, double scalar)
        {
            return new Coord(a.x / scalar, a.y / scalar);
        }

        public static Coord operator /(double scalar, Coord a)
        {
            return a / scalar;
        }

        public static bool operator ==(Coord a, Coord b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(Coord a, Coord b)
        {
            return !a.Equals(b);
        }

        #endregion
    }
}
